Current File : //usr/share/ghostscript/10.02.1/Resource/Init/pdf_main.ps |
% Copyright (C) 2001-2023 Artifex Software, Inc.
% All Rights Reserved.
%
% This software is provided AS-IS with no warranty, either express or
% implied.
%
% This software is distributed under license and may not be copied,
% modified or distributed except as expressly authorized under the terms
% of the license contained in the file LICENSE in this distribution.
%
% Refer to licensing information at http://www.artifex.com or contact
% Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
% CA 94129, USA, for further information.
%
% pdf_main.ps
% PDF file- and page-level operations.
/.setlanguagelevel where { pop 2 .setlanguagelevel } if
.currentglobal //true .setglobal
% ======================== Main program ======================== %
userdict begin
% Redefine 'run' so it recognizes PDF files.
systemdict begin
systemdict /OLDPDF known {
systemdict /OLDPDF get {
( **** WARNING ****\n) print
(The old, written in PostScript, PDF interpreter has been removed entirely.\n) print
(You should cease using -dOLDPF as it has no effect now.\n) print
(Continuing to process PDF file using the new, written in C, PDF interpreter.\n\n) print
} if
} if
systemdict /NEWPDF known {
systemdict /NEWPDF get not {
(The old, written in PostScript, PDF interpreter has been removed entirely.\n) print
(You should cease using -dNEWDPF as it has no effect now.\n) print
(Continuing to process PDF file using the new, written in C, PDF interpreter.\n) print
} if
} if
% PostScript interface to the ghostpdf C-based interpreter
%
% Promised or documented as used by customers
% ===========================================
% runpdf <file> runpdf -
% Called from the modified run operator (which copies stdin to a temp
% file if required). Calls runpdfbegin, check for PDF collections. Then
% calls process_trailer_attrs, runpdfpagerange, dopdfpages and runpdfend
% to process all the required pages.
%
% runpdfbegin <file> runpdfbegin -
% creates /pdfdict in userdict set userparmas (ProcessDSCComment
% setobjectformat to 0 /Page# /Page to null DSCPageCount to 0
% /PDFSave /null.
% calls pdfopen and then 'begin's the returned dictionary.
% Also disables page handler device.
%
% pdfgetpage <int> pdfgetpage <pagedict> | <null>
% int is a number from 1 to N indicating the desired page number from
% the PDF file. Returns a dictionary, apparently the /Page dictionary. Probably
% we only need the Boxes and Rotate key ? If this fails, returns a null object.
%
% pdfshowpage_init <int> pdfshowpage_init <dict>
% This simply adds 1 to the /DSCPageCount value in the supplied dictioanry
%
% pdfshowpage_setpage <pagedict> pdfshowpage_setpage <pagedict>
% Takes a dictionary as returned from pdfgetpage, extracts various
% parameters from it, and sets the media size for the page, taking into
% account the boxes, and requested Box, Rotate value and PDFFitPage.
%
% pdfshowpage_finish <pagedict> pdfshowpage_finish -
% Takes a dictionary as returned from pdfgetpage, renders the page content
% executes showpage to transfer the rendered content to the device.
%
% runpdfend -
% Uses currentdict as an argument to pdfclose, which closes /PDFfile. Also
% executes restore and various cleanup activities.
%
% pdfavailable - pdfavailable <bool>
% Determines if there is a PDF interpreter available
%
% Also Used by gsview 5
% =====================
% pdfopen <file> pdfopen <dict>
% According to the comments; open a PDF file and read the header, trailer
% and cross-reference. Calls pdfopenfile and 'begin's the returned dictionary
% calls pdfopencache and then closes the current dictionary.
% pdfopenfile appears to create a dictionary containing a load of stuff but
% most importantly, sets /PDFfile to the passed-in file. Probably also contains
% the Trailer dictionary.
%
% pdfclose <dict> pdfclose -
% The supplied dictionary contains /PDFfile and it executes closefile on that
% supplied file.
%
% Supplied by Ray to customer
% ============================
% pdfshowpage <pagedict> pdfshowpage
% Takes a dictionary returned from pdfgetpage and calls the pdfshowpage_init
% pdfshowpage_setpage, pdfshowpage_finish trio to start the page, set up the
% media and render the page.
%
% dopdfpages <int> <int> dopdfpages -
% The integers are the first and last pages to be run from the file. Runs a loop from
% the fist integer to the last. NOTE! If the current dictionary contains a PDFPageList
% array then we 'get' the entry from the array corresponding to the current loop
% index, and use that to determine whether we should draw the page. Otherwise we
% simply draw the page. Uses pdfshowpage to actually render the page.
%
% Additionallly useful ?
% =======================
% runpdfpagerange - runpdfpagerange <int> <int>
% Processes the PostScript /FirstPage, /LastPage and /PageList parameters to build an array
% of page numbers to run (if PageList is present) and a FirstPage and LastPage value.
% There seems no point in rewriting this, may as well use it to control page processing.
%
% Normal operation
% =================
% runpdf - runpdfbegin - pdfopen
% - process_trailer_attrs
% - runpdfpagerange
% - dopdfpages - pdfgetpage
% - pdfshowpage - pdfshowpage_init
% - pdfshowpage_setpage
% - pdfshowpage_finish
% - runpdfend - pdfclose
%
/DisablePageHandlerDevice
{
systemdict /FirstPage known
systemdict /LastPage known or
systemdict /PageList known or
{
<</DisablePageHandler //true>> setpagedevice
} if
} bind def
/EnablePageHandlerDevice
{
systemdict /FirstPage known
systemdict /LastPage known or
systemdict /PageList known or
{
<</DisablePageHandler //false>> setpagedevice
} if
} bind def
/newpdf_pdfformaterror { % <string> pdfformaterror -
(%stdout) (w) file
dup 3 -1 roll writestring flushfile
} bind executeonly def
% This routine sets up the transparency compositor requirement, and
% number of spot channels required (if any) in the device. This needs
% to be done before we set the media size, so that the erasepage is
% correct. Called with the dictionary returned from .PDFPageInfo
/newpdf_device_setup
{
2 dict exch % <<page dict>> << >>
dup /UsesTransparency get % << >> <<page dict>> bool
/PageUsesTransparency exch % << >> <<page dict>> /PageUsesTransparency bool
3 index 3 1 roll % << >> <<page dict>> <<info dict>> << >> /PageUsesTransparency bool
put % <</PageUsesTransparency bool>> <<page dict>>
currentpagedevice /PageSpotColors known {
/NumSpots get % <</PageUsesTransparency bool>> int
/PageSpotColors exch % <</PageUsesTransparency bool>> /PageSpotColors int
2 index 3 1 roll % <</PageUsesTransparency bool>> <<page dict>> /PageSpotColors int
put % <</PageUsesTransparency bool /PageSpotColors int >>
}{
pop
} ifelse
setpagedevice
}bind def
% Takes the selected Box as a parameter and scales it to fit the current media.
% If the amount of scaling would be reduced by rotating the content, then
% rotate the content.
% Caters for the box non-zero lower left x and y (ie cropping)
/NewPDF_FitPage
{
dup 2 get 1 index 0 get sub %urx -llx = width
1 index 3 get 2 index 1 get sub %ury - lly = height
currentpagedevice /PageSize get aload pop % Stack - box boxwidth boxheight mediawidth mediaheight
% Start by deciding if we should rotate the page to better fit the media.
4 copy eq
3 1 roll eq
or % square media or square page, no point in rotating
{
pop pop pop pop
//false
}
{
gt
{ % landscape media
ge
{ % landscape media, landscape box, no rotation
//false
}
{ % landscape media, portrait box, rotate it to landscape
//true
} ifelse
}
{ % portrait media
ge
{ % portrait media, landscape box, rotate it to portrait
//true
}
{ % portrait media, portrait box, no rotation
//false
} ifelse
}ifelse
} ifelse
% Store values for calculating scale, we do this here because we want to
% swap the media width and height (for the scale calculation) if we are
% rotating the page
1 index 2 get 2 index 0 get sub % urx -llx = width
2 index 3 get 3 index 1 get sub % ury - lly = height
currentpagedevice /PageSize get aload pop % Stack - box rotate boxwidth boxheight mediawidth mediaheight
5 -1 roll % Stack - box boxwidth boxheight mediawidth mediaheight rotate
% If we need to rotate, recalculate the box
{
90 rotate
0 2 index neg translate % move 0,0 to bottom right of media to account for rotation
exch % swap media width/height for scaling calculation
}if
% Now use the box and media values to calculate the required x/y scale factors
4 copy % Stack - box boxwidth boxheight mediawidth mediaheight boxwidth boxheight mediawidth mediaheight
3 -1 roll div % box boxwidth boxheight mediawidth mediaheight boxwidth mediawidth (mediaheight / boxheight)
3 1 roll % box boxwidth boxheight mediawidth mediaheight (mediaheight / boxheight) boxwidth mediawidth
exch % box boxwidth boxheight mediawidth mediaheight (mediaheight / boxheight) mediawidth boxwidth
div % box boxwidth boxheight mediawidth mediaheight (mediaheight / boxheight) (mediawidth / boxwidth)
% Stack - box boxwidth boxheight mediawidth mediaheight Yscale Xscale
% Centre the output on the media
2 copy % box boxwidth boxheight mediawidth mediaheight Yscale Xscale Yscale Xscale
gt {
exch pop % box boxwidth boxheight mediawidth mediaheight Xscale
dup 4 index mul % box boxwidth boxheight mediawidth mediaheight Xscale (boxheight * Xscale)
3 -1 roll sub dup
0 lt {-1 mul} if % box boxwidth boxheight mediawidth Xscale ((boxheight * Xscale) - mediaheight)
dup 0 ne {2 div} if % box boxwidth boxheight mediawidth Xscale (heightdiff / 2)
0 exch translate % box boxwidth boxheight mediawidth Xscale
} {
pop % box boxwidth boxheight mediawidth mediaheight Yscale
dup 5 index mul % box boxwidth boxheight mediawidth mediaheight Yscale (boxwidth * Yscale)
4 -1 roll sub dup
0 lt {-1 mul} if % box boxwidth boxheight mediaheight Yscale ((boxwidth * Yscale) - mediawidth)
dup 0 ne {2 div} if % box boxwidth boxheight mediawidth Yscale (widthdiff / 2)
0 translate % box boxwidth boxheight mediawidth Yscale
} ifelse
% Apply any 'offset' in the Box. Tht is, if the llx and lly are not 0, then we need to
% shift the origin of the content so that they become 0,0. We need to take into
% account the scaling from above.
4 index aload pop pop pop
neg 2 index mul exch neg 2 index mul exch
translate
dup scale % scale both axes the same
pop pop pop % remove the leftover boxwidth, boxheight and mediawidth/height
} bind def
/newpdf_get_media_box { % <pagedict> get_media_box <box> <bool>
dup /MediaBox known {
/MediaBox get
dup length 4 eq {
//true
}
{
pop
( **** Error: Page has an invalid /MediaBox attribute. Using the current page size.\n)
newpdf_pdfformaterror
( Output may be incorrect.\n) pdfformaterror
[ 0 0 currentpagedevice /PageSize get aload pop ]
//false
}ifelse
}
{
pop
( **** Error: Page has no /MediaBox attribute. Using the current page size.\n)
newpdf_pdfformaterror
( Output may be incorrect.\n) newpdf_pdfformaterror
[ 0 0 currentpagedevice /PageSize get aload pop ] //false
} ifelse
} bind executeonly def
% [llx lly urx ury] newpdf_check_empty_box [llx lly urx ury] true | false
% rturns true and the original array if its valid, otherwise returns false
/newpdf_check_empty_box
{
dup type /arraytype eq
{
dup aload pop
dup 3 index eq
{
//true
}
{
1 index 4 index eq
} ifelse
{
pop pop pop pop
( **** Warning: File has an empty Box parameter.\n) print
( Using the MediaBox instead.\n) print
( Output may be incorrect.\n) print flush
pop //false
}
{
pop pop pop pop
//true
} ifelse
}
{
( **** Warning: File has an invalid Box parameter.\n) print
( Using the MediaBox instead.\n) print
( Output may be incorrect.\n) print flush
pop //false
} ifelse
} bind executeonly def
/newpdf_get_any_box { % <pagedict> get_any_box <box name> <box>
//systemdict /UseBleedBox .knownget dup { and } if {
dup /BleedBox .knownget {
newpdf_check_empty_box
{
/BleedBox exch
} if
} if
} if
dup type /arraytype ne {
//systemdict /UseTrimBox .knownget dup { and } if {
dup /TrimBox .knownget {
newpdf_check_empty_box
{
/TrimBox exch
} if
} if
} if
} if
dup type /arraytype ne {
//systemdict /UseArtBox .knownget dup { and } if {
dup /ArtBox .knownget {
newpdf_check_empty_box
{
/ArtBox exch
} if
} if
} if
} if
dup type /arraytype ne {
//systemdict /UseCropBox .knownget dup { and } if {
dup /CropBox .knownget {
newpdf_check_empty_box
{
/CropBox exch
} if
} if
} if
} if
dup type /arraytype ne {
/MediaBox exch newpdf_get_media_box pop
} {
%% Complicated stuff. We need to use the 'Box' we identified (if any), but we
%% need to clamp the boundaries of the 'Box' to the MediaBox. This appears to
%% be what Acrobat does. The complication arises because the Box values don't
%% have to be sensibly positive, its permissible to have the MediaBox set up
%% so that it extends down and left instead of up and right. We take care of the
%% content when we st up the CTM, but we do need to make sure that we clamp
%% the BoundingBox, and that means we need to take direcitonality into account...
aload pop
6 -1 roll newpdf_get_media_box { % /SomeBox x0 y0 x1 y1 [MediaBox]
aload pop % /SomeBox x0 y0 x1 y1 X0 Y0 X1 Y1
%% Start with the width, get the X0 and X1 values of the MediaBox
3 index % /SomeBox x0 y0 x1 y1 X0 Y0 X1 Y1 X0
2 index % /SomeBox x0 y0 x1 y1 X0 Y0 X1 Y1 X0 X1
gt {
%% Media extends to left
4 -1 roll % /SomeBox x0 y0 x1 y1 Y0 X1 Y1 X0
8 -1 roll % /SomeBox y0 x1 y1 Y0 X1 Y1 X0 x0
.min % /SomeBox y0 x1 y1 Y0 X1 Y1 mX0
7 1 roll % /SomeBox mX0 y0 x1 y1 Y0 X1 Y1
exch % /SomeBox mX0 y0 x1 y1 Y0 Y1 X1
5 -1 roll % /SomeBox mX0 y0 y1 Y0 Y1 X1 x1
.max % /SomeBox mX0 y0 y1 Y0 Y1 mX1
5 1 roll % /SomeBox mX0 mX1 y0 y1 Y0 Y1
}{
%% Media extends to right
4 -1 roll % /SomeBox x0 y0 x1 y1 Y0 X1 Y1 X0
8 -1 roll % /SomeBox y0 x1 y1 Y0 X1 Y1 X0 x0
.max % /SomeBox y0 x1 y1 Y0 X1 Y1 mX0
7 1 roll % /SomeBox mX0 y0 x1 y1 Y0 X1 Y1
exch % /SomeBox mX0 y0 x1 y1 Y0 Y1 X1
5 -1 roll % /SomeBox mX0 y0 y1 Y0 Y1 X1 x1
.min % /SomeBox mX0 y0 y1 Y0 Y1 mX1
5 1 roll % /SomeBox mX0 mX1 y0 y1 Y0 Y1
} ifelse
%% Now deal with the height
2 copy % /SomeBox mX0 mX1 y0 y1 Y0 Y1 Y0 Y1
gt {
%% Media extends down
exch % /SomeBox mX0 mX1 y0 y1 Y1 Y0
4 -1 roll % /SomeBox mX0 mX1 y1 Y1 Y0 y0
.min % /SomeBox mX0 mX1 y1 Y1 mY0
3 1 roll % /SomeBox mX0 mX1 mY0 y1 Y1
.max % /SomeBox mX0 mX1 mY0 mY1
}{
%% Media extends up
exch % /SomeBox mX0 mX1 y0 y1 Y1 Y0
4 -1 roll % /SomeBox mX0 mX1 y1 Y1 Y0 y0
.max % /SomeBox mX0 mX1 y1 Y1 mY0
3 1 roll % /SomeBox mX0 mX1 mY0 y1 Y1
.min % /SomeBox mX0 mX1 mY0 mY1
} ifelse
exch % /SomeBox mX0 mX1 mY1 mY0
3 1 roll % /SomeBox mX0 mY0 mX1 mY1
} {
pop
} ifelse
4 array astore % /SomeBox [mX0 mY0 mX1 mY1]
} ifelse
} bind executeonly def
% This routine is used to set the PostScript /PageSize from the requested Box of the
% PDF file. Much of this is copied from the old pdfshowpage_setpage routine which
% is used for the PDF interpreter written in PostScript.
%
% Called with a dictionary containing the PDF page information.
%
/newpdf_set_pagesize
{
% Stack: pdfpagedict
% Don't understand this at all, removed for now replace if we ever figure out
% what it's for.
% Only lock in Orientation if we need to for pdfmarks
% .writepdfmarks { /Orientation 0 def } if
dup
dup newpdf_get_any_box % Stack: pdfpagedict pdfpagedict /BoxName [box]
% Check that the box is 'normal' and make it so if not
% Also check the array is 4 elements and if it isn't then use the
% current PageSize. The normalise arithmetic assumes the array is a 4
% element array.
dup length 4 ne {
% Invalid size of mediabox
pop currentpagedevice /PageSize get
} {
aload 5 1 roll % array x1 y1 x2 y2
exch % array x1 y1 y2 x2
4 -1 roll % array y1 y2 x2 x1
2 copy gt {
exch % array y1 y2 xmin xmax
} if
4 -2 roll % array xmin xmax y1 y2
2 copy gt {
exch % array xmin xmax ymin ymax
} if
4 1 roll
exch 4 -1 roll
5 -1 roll
astore
} ifelse
//systemdict /PDFFitPage known {
NewPDF_FitPage
} {
6 dict begin % for setpagedevice
% Set the page size.
2 index /UserUnit known
{
systemdict /NoUserUnit .knownget not {//false} if
{
2 index /UserUnit undef
% No scaling required, calculate PageSize as the width and height of the box
aload 5 1 roll % box llx lly urx ury
2 index sub % box llx lly urx (ury - lly)
exch 3 index sub % box llx lly (ury - lly) (urx - llx)
exch
}
{
2 index /UserUnit get
/PassUserUnit /GetDeviceParam .special_op {exch pop} {//false} ifelse {
[ /UserUnit 3 -1 roll .pdfputparams pop pop
% No media scaling required, calculate PageSize as the width and height of the box
aload 5 1 roll % box llx lly urx ury
2 index sub % box llx lly urx (ury - lly)
exch 3 index sub % box llx lly (ury - lly) (urx - llx)
exch
}{
% The PageSize needs to be multiplied too
exch aload 5 1 roll % UserUnit box llx lly urx ury
2 index sub exch % UserUnit box llx lly (ury - lly) urx
3 index sub % UserUnit box llx lly boxheight boxwidth
5 index mul % UserUnit box llx lly boxheight (Boxwidth*UserUnit)
exch
5 index mul % UserUnit box llx lly (Boxwidth*UserUnit) (boxheight*UserUnit)
4 -2 roll
5 index mul exch % UserUnit box (Boxwidth*UserUnit) (boxheight*UserUnit) (lly*UserUnit) llx
5 index mul exch % UserUnit box (Boxwidth*UserUnit) (boxheight*UserUnit) (llx*UserUnit) (lly*UserUnit)
4 2 roll % UserUnit box (llx*UserUnit) (lly*UserUnit) (Boxwidth*UserUnit) (boxheight*UserUnit)
6 -1 roll pop % box (llx*UserUnit) (lly*UserUnit) (Boxwidth*UserUnit) (boxheight*UserUnit)
} ifelse
} ifelse
}
{
/PassUserUnit /GetDeviceParam .special_op {
exch pop {
[ /UserUnit 1 .pdfputparams pop pop
} if
}if
% No scaling required, calculate PageSize as the width and height of the box
aload 5 1 roll % box llx lly urx ury
2 index sub % box llx lly urx (ury - lly)
exch 3 index sub % box llx lly (ury - lly) (urx - llx)
exch
} ifelse
% handle page rotation here
6 index /Rotate known {
% PDF page rotation runs clockwise and must be a multiple of 90, unlike PostScript.....
% convert Rotate into a quadrant number
6 index /Rotate get 90 div cvi
% Make sure quadrant is 0-3
dup 0 lt {
% Negative rotation... Turn it into a positive rotation
% Again, limit the quadrant to -0 to -3
dup -4 lt {4 mod} if
% Adding 360 degrees results in the same positive rotation
4 add
} if
dup 3 gt {4 mod} if
dup 0 eq
{
pop
2 array astore /PageSize exch def
currentdict end setpagedevice
neg exch neg exch
translate
}
{
dup 1 eq
{
pop
exch 4 2 roll exch 4 2 roll
2 array astore dup /PageSize exch def
currentdict end setpagedevice
270 rotate
1 get add neg exch neg
translate
}
{
2 eq
{
2 array astore dup /PageSize exch def
currentdict end setpagedevice
180 rotate
aload pop 3 -1 roll add neg 3 1 roll add neg exch
translate
}
{
exch 4 2 roll exch 4 2 roll
2 array astore dup /PageSize exch def
currentdict end setpagedevice
90 rotate
0 get 3 -1 roll add
neg exch neg exch
translate
} ifelse
} ifelse
} ifelse
}
{
2 array astore /PageSize exch def
currentdict end setpagedevice
neg exch neg exch
translate
}ifelse
% scale the co-ordinate system by the UserUnit
% We have to do this after setting the PageSize, because
% setpagedevice does an implicit initgraphics, resetting the CTM.
%
//systemdict /NoUserUnit .knownget not { //false } if not
{
2 index /UserUnit known {
/PassUserUnit /GetDeviceParam .special_op {
exch pop not
} {
true
}ifelse
% If PassUserUnit isn't handled by the device, or it returns 'false'
% then scale the content by the UserUnit. Otherwise do not scale.
{
2 index /UserUnit get
dup scale
} if
} if
} if
} ifelse
pop % The box
% If we are using the MediaBox (and only the MediaBox) then we
% want to clip to the CropBox, if there is one present
/MediaBox eq {
dup /CropBox known {
/CropBox get
aload pop 2 index sub exch 3 index sub exch rectclip
} {
pop
} ifelse
} {
pop
}ifelse
pop
} bind executeonly def
% This routine checks a list of known/implemented command line switches to see if they
% have been defined in the PostScript environment. If they have we construct a dictionary
% containing the names and their values, and return it. That dictionary can then be
% passed to a custom PostScript operator and used to configure the PDF interpreter.
%
% NB device parameters will already have been sent to the device and used to configure it
% so here we should only handle parameters which control the behaviour of the interpreter.
%
/PDFSwitches [ /QUIET /PDFPassword /PDFDEBUG /PDFSTOPONERROR /PDFSTOPONWARNING /NOTRANSPARENCY /FirstPage /LastPage
/PDFNOCIDFALLBACK /NO_PDFMARK_OUTLINES /NO_PDFMARK_DESTS /PDFFitPage /Printed /UsePDFX3Profile
/UseBleedBox /UseCropBox /UseArtBox /UseTrimBox /ShowAcroForm /ShowAnnots /PreserveAnnots
/NoUserUnit /RENDERTTNOTDEF /DOPDFMARKS /PDFINFO /ShowAnnotTypes /PreserveAnnotTypes
/CIDFSubstPath /CIDFSubstFont /SUBSTFONT /IgnoreToUnicode /NONATIVEFONTMAP /PreserveMarkedContent /OutputFile
/PreserveDocView /PreserveEmbeddedFiles ] def
/newpdf_gather_parameters
{
10 dict begin
//PDFSwitches {
dup where
{ exch dup 3 1 roll get def }
{ pop } ifelse
} forall
% This isn't a command line parameter, we track it internally, but we need to
% send it to the interpreter. It is used to 'offset' the page Dest for Link
% annotations and Outlines by the numebr of pages processed so far.
/PageCount /CumulativePageCount where { /CumulativePageCount get }{currentpagedevice /PageCount get} ifelse def
currentdict end
} bind executeonly def
currentdict /PDFSwitches undef
/pdfpagecount
{
currentdict /PDFInfo known
{
PDFInfo
}
{
PDFFile //null eq not
{
PDFSTOPONERROR
{
PDFFile .PDFInfo //false
}
{
PDFFile {.PDFInfo} stopped
} ifelse
}
{
//true
}ifelse
{
<</NumPages 0>>
} if
} ifelse
dup /NumPages known
{
/NumPages get
}
{
pop 0
} ifelse
}bind def
/runpdfpagerange
{
/PageList where {
pop
PageList pdfpagecount .PDFparsePageList
dup 0 eq { % No ranges, error
(\n **** Error: Invalid PageList: ) print
PageList print
(\n No pages will be processed.) = flush
/runpdfpagerange cvx /syntaxerror signalerror
} if
array astore % move integer triples from the stack to the array
% newpdf PDFPageList is an array of 3 elements per range: even/odd flag, start, end
/PDFPageList exch def
QUIET not {
(Processing pages ) print PageList =only (.) = flush
} if
1 pdfpagecount % dummy parameters for newpdf_dopages
}{
/FirstPage where {
pop FirstPage dup pdfpagecount gt {
(\nRequested FirstPage is greater than the number of pages in the file: ) print
pdfpagecount = flush
} if
} {
1
} ifelse
/LastPage where {pop LastPage pdfpagecount .min } { pdfpagecount } ifelse
1 index 1 index gt {
( No pages will be processed \(FirstPage > LastPage\).) = flush
} {
QUIET not {
(Processing pages ) print 1 index =only ( through ) print dup =only
(.) = flush
} if
} ifelse
} ifelse
} bind def
/runpdf_collection_entry
{
<</PreserveDocView false>> runpdfbegin_with_params % <file> runpdfbegin -
PDFInfo type /dicttype eq
{
PDFInfo /Collection known
{
PDFInfo /Collection get
pdfclose
dup length 1 sub 0 2 3 -1 roll
{
1 index exch get (r) file runpdf_collection_entry
} for
pop
}
{
process_trailer_attrs % - process_trailer_attrs -
runpdfpagerange % - runpdfpagerange <int> <int>
dopdfpages % <int> <int> dopdfpages -
runpdfend % - runpdfend -
} ifelse
} {
pop pop
}ifelse
} bind def
% <file> runpdf -
/newpdf_runpdf
{
runpdfbegin % <file> runpdfbegin -
PDFInfo type /dicttype eq
{
PDFInfo /Collection known
{
PDFInfo /Collection get
pdfclose
dup length 1 sub 0 2 3 -1 roll
{
1 index exch get (r) file runpdf_collection_entry
} for
pop
% We need to do this, even though we called pdfclose above, in order to clean
% up our dictionary and restore the state. We can't use runpdfend above because
% if we do then we will delete all the temporary files containing the embedded
% PDF files.
runpdfend
}
{
process_trailer_attrs % - process_trailer_attrs -
runpdfpagerange % - runpdfpagerange <int> <int>
dopdfpages % <int> <int> dopdfpages -
runpdfend % - runpdfend -
} ifelse
} {
pop pop
}ifelse
} bind def
% start a PDF file, and specify interpreter parameters to be used
% to override any command line ones.
% Mostly for the benefit of PDF Collections where we want to disable
%preserving DocView information for pdfwrite.
%
% file <dict> runpdfbegin_with_params -
/runpdfbegin_with_params
{
/pdfdict 10 dict def
pdfdict begin
currentpagedevice /PageCount get
/CumulativePageCount exch def
% This is for the benefit of pdf2dsc which assumes it will be present
/Trailer << >> def
/PDFSave save def
% Define these in the current dictionary, if anyone uses
% systemdict then this will be defeated so we'll add extra
% code to the systemdict definitions as well.
% /pdfpagecount /newpdf_pagecount load def
% /pdfgetpage /newpdf_pdfgetpage load def
% /pdfshowpage /newpdf_pdfshowpage load def
% /pdfshowpage_init /newpdf_pdfshowpage_init load def
% /pdfshowpage_setpage /newpdf_pdfshowpage_setpage load def
% /pdfshowpage_finish /newpdf_pdfshowpage_finish load def
% /pdfopen /newpdf_pdfopen load def
% /pdfclose /newpdf_pdfclose load def
% /dopdfpages /newpdf_dopdfpages load def
% /runpdfend /newpdf_runpdfend load def
% /runpdfpagerange /newpdf_runpdfpagerange load def
% /process_trailer_attrs /newpdf_process_trailer_attrs load def
% These are also for the benefit of pdf2dsc, which assumes they
% are available.
/knownoget {2 copy known {get //true}{pop pop //false}ifelse} bind def
/pget {2 copy known {get //true}{pop pop //false}ifelse}bind def
newpdf_gather_parameters % <parameter overrides dict> <parameter dict>
exch {2 index 3 1 roll put} forall
PDFSTOPONERROR
{
.PDFInit
/PDFFile exch def
pdfopen
/PDFInfo exch def
pop
}
{
{.PDFInit} stopped
{
( **** Error: Failed to initialise PDF interpreter.\n) newpdf_pdfformaterror
/PDFFile //null def
/PDFInfo //null def
}
{
/PDFFile exch def
pdfopen
/PDFInfo exch def
pop
}ifelse
}ifelse
} bind def
% -file- runpdfbegin -
/runpdfbegin
{
<<>> runpdfbegin_with_params
} bind def
/pdfgetpage
{
dup 1 sub
PDFSTOPONERROR
{
PDFFile exch .PDFPageInfo
dup 3 -1 roll
/Page# exch put
}
{
PDFFile exch {.PDFPageInfo} stopped
{
pop pop
( **** Error: Couldn't get page info.\n) newpdf_pdfformaterror
( Output may be incorrect.\n) newpdf_pdfformaterror
//null
}{
dup 3 -1 roll
/Page# exch put
} ifelse
}ifelse
} bind def
/process_trailer_attrs
{
}bind def
/pdfshowpage_init
{
} bind def
/pdfshowpage_setpage
{
dup newpdf_device_setup
dup newpdf_set_pagesize
} bind def
/pdfshowpage_finish
{
PDFSTOPONERROR
{
/Page# get PDFFile exch 1 sub .PDFDrawPage
showpage
}
{
/Page# get PDFFile exch 1 sub {.PDFDrawPage} stopped
{
pop pop
( **** Error: Page drawing error occurred.\n) newpdf_pdfformaterror
( Output may be incorrect.\n) newpdf_pdfformaterror
} if
{showpage} stopped
{
( **** Error: Page drawing error occurred.\n) newpdf_pdfformaterror
( Could not draw this page at all, page will be missing in the output.\n) newpdf_pdfformaterror
} if
}ifelse
} bind def
/pdfshowpage
{
/PDFINFO where {/PDFINFO get}{//false}ifelse
{
/Page# get PDFFile exch 1 sub {.PDFDrawPage} stopped pop
}
{
pdfshowpage_init
pdfshowpage_setpage
pdfshowpage_finish
}ifelse
} bind def
/runpdfend
{
% Get the accumulated count of pages processed so far
% and the number of pages in this file. Do this before
% we close the file and restore the state. Save the values
% on the stack.
pdfpagecount
CumulativePageCount
pdfclose
PDFSave restore
end % pdfdict
% add the number of pages in this file to the accumulated count,
% and store that in the device for later reuse. This allows us to
% add the number of pages already in the output to the 'Dest' of
% Outlines and Link annotations.
add <</PageCount 3 -1 roll >> setpagedevice
} bind def
/pdfopen
{
dup PDFFile //null ne
{
PDFSTOPONERROR
{
PDFFile .PDFStream
PDFFile .PDFInfo
}
{
PDFFile {.PDFStream} stopped
{
pop pop
( **** Error: Couldn't initialise file.\n) newpdf_pdfformaterror
( Output may be incorrect.\n) newpdf_pdfformaterror
<</NumPages 0>>
}
{
PDFFile {.PDFInfo} stopped
{
pop
( **** Error: Couldn't get page information.\n) newpdf_pdfformaterror
( Output may be incorrect.\n) newpdf_pdfformaterror
<</NumPages 0>>
} if
} ifelse
}ifelse
}
{
pop
<</NumPages 0>>
} ifelse
} bind def
/pdfclose
{
PDFSTOPONERROR
{
PDFFile .PDFClose
}
{
PDFFile {.PDFClose} stopped
{
pop
} if
}ifelse
} bind def
/pdfavailable
{
.PDFAvailable
} bind def
% <int> <int> dopdfpages -
% First Page and then LastPage
% If PDFPageList array exists, the parameters are 1 pdfpagecount and are ignored.
/dopdfpages
{
//DisablePageHandlerDevice exec
%% If we have a array of page ranges to render, use it.
/PDFPageList where {
pop
pop pop % don't use dummy parameters
PDFPageList
% process the ranges (3 elements per range)
0 3 2 index length 1 sub {
1 index 1 index get % even = 2, odd = 1 any = 0
2 index 2 index 1 add get % start of range
exch
3 index 3 index 2 add get % end of range
exch
% stack: start end even/odd
0 eq { 1 } { 2 } ifelse
2 index 2 index gt { neg } if % negate increment for reverse range
exch
{
pdfgetpage
dup //null ne {
pdfshowpage
} {
PDFSTOPONERROR {
/dopdfpages cvx /syntaxerror signalerror
} {
pop pop
( **** Error: page) newpdf_pdfformaterror
( not found.\n) newpdf_pdfformaterror
} ifelse
} ifelse
} for
pop % for loop index
} for
pop % done with array
} {
% else, Process the pages given by the FirstPage, LastPage
1 exch
{
pdfgetpage
dup //null ne {
pdfshowpage
} {
PDFSTOPONERROR {
/dopdfpages cvx /syntaxerror signalerror
} {
pop pop
( **** Error: page) newpdf_pdfformaterror
( not found.\n) newpdf_pdfformaterror
} ifelse
} ifelse
} for
} ifelse
//EnablePageHandlerDevice exec
} bind def
/.runps /run load def
/run {
dup type /filetype ne { (r) file } if
% skip leading whitespace characters (actually anything less than or equal to <sp>)
{ dup ( ) .peekstring not { //false exit } if
dup 0 get 32 le { pop dup read pop pop } { //true exit } ifelse
} loop
exch pop
{
% Appletalk PAP sends short strings with %! header expecting a response.
% 'gv' swallows the %!PS line, then sends DSC comments beginning with %%
% and also waits for a response. The following avoids those hangs.
dup 2 string .peekstring pop dup (%!) eq exch (%%) eq or {
cvx .runps
} {
dup 1023 string .peekstring pop
% "1024 string" exceeds current %stdin buffer
% Valid PDF file cannot be smaller than 400 bytes.
(%PDF-) search {
3 1 roll pop pop
dup (%!PS) search not {
length 0 ne {
1 index exch readstring pop pop
(%stderr) (w) file dup
( **** Warning: File has some garbage before %PDF- .\n)
writestring flushfile
PDFSTOPONWARNING {
/run cvx /unregistered signalerror
} if
} {
pop
} ifelse
dup (%stdin) (r) file eq {
% Copy PDF from stdin to temporary file then run it.
//null (w+) /.tempfile .systemvar exec exch 3 1 roll
% stack: tempname stdin tempfile
64000 string
{
% stack: tempname stdin tempfile string
2 index 1 index readstring
exch 3 index exch writestring
not { exit } if
}
loop
pop exch closefile
% stack: tempname tempfile
dup 0 setfileposition
dup
pdfavailable {
runpdf
}{
closefile
(%stderr) (w) file ( **** ERROR: No PDF interpreter available, unable to process PDF files as input.\n)writestring
} ifelse
closefile deletefile
} {
pdfavailable {
runpdf
}{
closefile
(%stderr) (w) file ( **** ERROR: No PDF interpreter available, unable to process PDF files as input.\n)writestring
} ifelse
} ifelse
} {
pop pop pop pop cvx .runps % (%!PS) found first
} ifelse
} {
pop cvx .runps % (%PDF-) not found
} ifelse
} ifelse
} {
closefile % file was empty
} ifelse
} bind odef
currentdict /runpdfstring .undef
currentdict /DisablePageHandlerDevice undef
currentdict /EnablePageHandlerDevice undef
% Copy stream to an external temporary file and
% return the file name as PS name.
/copy_embedded_file {
//true resolvestream % strm
dup 1023 string .peekstring pop % "1024 string" exceeds current %stdin buffer
dup length 400 ge { % Valid PDF file cannot be smaller than 400 bytes.
(%PDF-) search {
pop pop pop //true
} {
pop //false
} ifelse
} {
pop //false
} ifelse {
//null (w) /.tempfile % strm (name) null (w) /.tempfile
.systemvar exec % strm (name) file
3 -1 roll % (name) file strm
32768 string % (name) file strm (buf)
{ 3 copy readstring % (name) file strm (buf) file (data) bool
3 1 roll % (name) file strm (buf) bool file (data)
writestring % (name) file strm (buf) bool
not { exit } if
} loop
pop closefile % (name) file
closefile % (name)
cvn % /name
} {
closefile
} ifelse
} bind executeonly def
% Recursively enumerate /Names entries
% <node> pdf_collection_names /temp_file_name ...
/pdf_collection_names {
dup /Names knownoget {
exch pop
{ oforce
dup type /dicttype eq {
/EF knownoget {
/F knownoget {
copy_embedded_file
} if
} if
} {
pop
} ifelse
} forall
} {
/Kids knownoget {
{ oforce
dup //null ne {
pdf_collection_names
} {
pop
} ifelse
} forall
} if
} ifelse
} bind executeonly def
/runpdf { % <file> runpdf -
dup type /filetype eq
{
dup
PDFSTOPONERROR
{
newpdf_runpdf
}
{
{newpdf_runpdf} stopped
{
( **** Error: PDF interpreter encountered an error processing the file.\n) pdfformaterror
} if
}ifelse
closefile
}
{
( **** Error: Attempt to process something other than a file object in runpdf.\n) pdfformaterror
} ifelse
} bind executeonly odef
end % systemdict
% Redefine the procedure that the C code uses for running piped input.
% It is OK to use { (%stdin) run } here, because a startjob cannot occur.
/.runstdin {
{ (%stdin) run } execute0
} bind executeonly def
end % userdict
% ------ Transparency support ------ %
systemdict /ALLOWPSTRANSPARENCY get
{
/.setopacityalpha
{
/.setfillconstantalpha where
{
pop
( **** WARNING: .setopacityalpha is deprecated (as of 9.53.0) and will be removed in a future release\n) print
( **** See .setfillconstantalpha/.setalphaisshape for the improved solution\n) print flush
false .setalphaisshape
dup .setfillconstantalpha
.setstrokeconstantalpha
}
{
/.setopacityalpha /undefined cvx signalerror
} ifelse
} bind def
/.setshapealpha
{
/.setfillconstantalpha where
{
pop
( **** WARNING: .setshapealpha is deprecated (as of 9.53.0) and will be removed in a future release.\n) print
( **** See .setfillconstantalpha/.setalphaisshape for the improved solution\n) print flush
true .setalphaisshape
dup .setfillconstantalpha
.setstrokeconstantalpha
}
{
/.setshapealpha /undefined cvx signalerror
} ifelse
} bind def
} if
.setglobal
%% This list of operators are used internally by various parts of the Ghostscript PDF interpreter.
%% Since each operator is a potential security vulnerability, and any operator listed here
%% is not required once the initislisation is complete and functions are bound, we undefine
%% the ones that aren't needed at runtime.
[
/.setdistillerparams
] systemdict .undefinternalnames
% The following are split out allowing control via ALLOWPSTRANSPARENCY command line param
% The examples/transparency_example.ps uses some of these (on the first line).
[
/.pushpdf14devicefilter /.poppdf14devicefilter /.setstrokeconstantalpha /.setfillconstantalpha /.endtransparencygroup
/.currentblendmode /.currenttextknockout /.begintransparencytextgroup
/.endtransparencytextgroup /.begintransparencymaskgroup /.begintransparencymaskimage /.begintransparencypagegroup
/.endtransparencymask /.image3x /.abortpdf14devicefilter /.setstrokeconstantalpha /.setfillconstantalpha /.setalphaisshape /.currentalphaisshape
% undefining these causes errors/incorrect output
%/.setblendmode /.begintransparencygroup /.settextknockout /.setstrokeoverprint /.setfilloverprint
%/.currentstrokeoverprint /.currentfilloverprint /.currentfillconstantalpha /.currentstrokeconstantalpha
%/.setSMask /.currentSMask
] systemdict dup /ALLOWPSTRANSPARENCY get {pop pop}{.undefinternalnames}ifelse